/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.execution;

import com.softwarecraftsmen.orogeny.BuildScript;
import com.softwarecraftsmen.orogeny.BuildScript.BuildSyntaxException;
import static com.softwarecraftsmen.orogeny.execution.OrogenyProcessor.isOrogenyAnnotation;
import com.softwarecraftsmen.orogeny.properties.Evaluatable;
import com.softwarecraftsmen.orogeny.properties.FixedValueProperty;
import com.softwarecraftsmen.orogeny.properties.PropertyName;
import static com.softwarecraftsmen.orogeny.properties.PropertyNamePrefix.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.lang.String.format;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import static java.util.Locale.UK;
import java.util.Map;

public final class AllProperties extends LinkedHashMap<PropertyName, Evaluatable>
{
	private final Class<? extends BuildScript> buildScriptClass;
	private final Map<PropertyName, String> propertiesToLines;

	public AllProperties(final @NotNull Class<? extends BuildScript> buildScriptClass, final @NotNull Map<PropertyName, Evaluatable> commandLineProperties)
	{
		this.buildScriptClass = buildScriptClass;
		this.propertiesToLines = new LinkedHashMap<>();
		putEnvironmentProperties();
		putSystemProperties();
		putCommandLineProperties(commandLineProperties);
		putOrogenyProperties();
	}

	private void putEnvironmentProperties()
	{
		final UpperCaseEnvironmentVariableOnWindowsProcessor processor = new UpperCaseEnvironmentVariableOnWindowsProcessor(buildScriptClass);
		for (Map.Entry<String, String> pair : java.lang.System.getenv().entrySet())
		{
			final String value = pair.getValue();
			final String environmentVariableName = processor.transformEnvironmentVariableName(pair.getKey());
			put(environment.property(environmentVariableName), new FixedValueProperty(value == null ? "" : value));
		}
	}

	private void putSystemProperties()
	{
		for (Map.Entry<Object, Object> pair : java.lang.System.getProperties().entrySet())
		{
			final Object value = pair.getValue();
			put(system.property((String) pair.getKey()), new FixedValueProperty(value == null ? "" : (String) value));
		}
	}

	private void putCommandLineProperties(final @NotNull Map<PropertyName, Evaluatable> commandLineProperties)
	{
		for (Map.Entry<PropertyName, Evaluatable> pair : commandLineProperties.entrySet())
		{
			put(pair.getKey(), pair.getValue());
		}
	}

	private void putOrogenyProperties()
	{
		final Annotation[] annotations = buildScriptClass.getAnnotations();
		for (Annotation annotation : annotations)
		{
			if (isOrogenyAnnotation(annotation))
			{
				put(orogeny.property(annotation.annotationType().getSimpleName()), new FixedValueProperty(new OrogenyProcessor(annotation).value()));
			}
		}
	}

	@Nullable
	public Evaluatable put(final @NotNull PropertyName propertyName, final @NotNull Evaluatable property)
	{
		return put(propertyName, property, "No Line");
	}

	@Nullable
	public Evaluatable put(final @NotNull PropertyName propertyName, final @NotNull Evaluatable property, final @NotNull String currentLine)
	{
		if (containsKey(propertyName))
		{
			throw new PropertyAlreadyDefinedException(propertyName);
		}
		propertiesToLines.put(propertyName, currentLine);
		return super.put(propertyName, property);
	}

	public final class PropertyAlreadyDefinedException extends BuildSyntaxException
	{
		public PropertyAlreadyDefinedException(final @NotNull PropertyName propertyName)
		{
			super(format(UK, "Property %1$s previously defined at line %2$s", propertyName, propertiesToLines.get(propertyName)));
		}
	}
}
